package MapLogic;

import java.util.ArrayList;

import Exceptions.InvalidArgument;
import GameElements.CTowerDefenseElement;
import IO.Path;
import IO.Waypoint;

/**
 * This class is used to represent Map Elements granting access during runtime
 * on map (tower or graphic and way path) elements. It also provides methods to
 * add, set and remove map elements.
 * 
 * @author Mohamad
 * 
 */
public class CTowerDefenseMapLogic {

	/**
	 * WayPaths: Used to save every WayPath on map from Spawn to Finish.
	 */
	private ArrayList<Path> WayPaths = new ArrayList<Path>();
	/**
	 * mapElements: Contains char value of every mapelement (see Documentation)
	 */
	private char[][] mapElements;
	/**
	 * maxX, maxY: Size of (intern) GameBoard
	 */
	private int maxX, maxY;
	/**
	 * Saves every tower generated (build) during runtime of game
	 */
	private ArrayList<CTowerDefenseElement> TowerOnMap = new ArrayList<CTowerDefenseElement>();

	/**
	 * Initializes intern MapLogic representing every field on gameboard
	 * 
	 * @param mapX
	 *            max X size of gameboard
	 * @param mapY
	 *            max Y size of gameboard
	 * @throws InvalidArgument
	 *             if maxX or maxY is negative
	 */
	public CTowerDefenseMapLogic(int mapX, int mapY) throws InvalidArgument {
		if (mapX <= 0 || mapY <= 0)
			throw new InvalidArgument();
		maxX = mapX;
		maxY = mapY;
		mapElements = new char[mapX][mapY];
	}

	/**
	 * 
	 */
	public CTowerDefenseMapLogic() {
		// TODO Auto-generated constructor stub
	}

	public ArrayList<Path> getWayPath() {
		return WayPaths;
	}

	public void setWayPath(ArrayList<Path> wayPath) {
		WayPaths = wayPath;
	}

	public char[][] getMapElements() {
		return mapElements;
	}

	public void setMapElements(char[][] mapElements) {
		this.mapElements = mapElements;
	}

	/**
	 * Returns the intern representation of the field in a char value
	 * 
	 * @param posX
	 *            X position of the field you request
	 * @param posY
	 *            Y position of the field you request
	 * @return field representation in char value (see Docu)
	 * @throws InvalidArgument
	 *             If PosX or PosY is negative or bigger than maxX or maxY
	 */
	public char getMapElementValue(int posX, int posY) throws InvalidArgument {
		if (posX > mapElements.length || posX < 0
				|| posY > mapElements[0].length || posY < 0)
			throw new InvalidArgument();
		for(int i = 0;i<mapElements.length;i++)
		{
			for(int j=0;j<mapElements[0].length;j++)
				System.out.print(mapElements[i][j]);
			System.out.print('\n');
		}
		
		return mapElements[posX][posY];
	}

	/**
	 * Returns the Path with the given ID
	 * 
	 * @param id
	 *            The ID of the WayPath you request
	 * @return Path
	 * @throws InvalidArgument
	 *             If id is negative
	 */
	public Path getWayPathByID(int id) throws InvalidArgument {
		if (id < 0)
			throw new InvalidArgument();
		return WayPaths.get(id);
	}

	/**
	 * Returns the Path that has the spawnpoint (x,y)
	 * 
	 * @param x
	 *            the X Coordinate of the spawnpoint
	 * @param y
	 *            the Y Coordinate of the spawnpoint
	 * @return Path that got the spawnpoint(x,y)
	 * @throws InvalidArgument
	 *             If x or y is negative or bigger than maxX or maxY
	 * 
	 */
	public Path getWayPathByPos(int x, int y) throws InvalidArgument {
		if (x > mapElements.length || x < 0 || y > mapElements[0].length
				|| y < 0)
			throw new InvalidArgument();
		for (int i = 0; i < WayPaths.size(); i++) {
			Waypoint _wp = WayPaths.get(i).getWayPoint(0);
			if (_wp.getX() == x && _wp.getY() == y)
				return WayPaths.get(i);
		}
		return null;
	}

	/*
	 * public boolean addWayPath(int[][] wp) throws InvalidArgument { if(wp ==
	 * null) throw new InvalidArgument(); for(int i=0;i<WayPaths.size();i++)
	 * if(WayPaths.get(i).CheckWayPoints(wp) == true) return false; CTDWayPath
	 * newWayPath = new CTDWayPath(); newWayPath.AddNewWayPoints(wp);
	 * WayPaths.add(newWayPath); return true; }
	 */

	/**
	 * Adds a new Tower
	 * 
	 * @param _ctd
	 *            CTowerDefenseElement with X and Y setted
	 * @throws InvalidArgument
	 *             If x or y Coordinate of Tower is negative or bigger than maxX
	 *             or maxY
	 */
	public void addNewMapElement(CTowerDefenseElement _ctd)
			throws InvalidArgument {
		if (_ctd.getPosX() > mapElements.length || _ctd.getPosX() < 0
				|| _ctd.getPosY() > mapElements[0].length || _ctd.getPosY() < 0)
			throw new InvalidArgument();
		mapElements[_ctd.getPosX()][_ctd.getPosY()] = 't';
		getTowerOnMap().add(_ctd);
	}

	/**
	 * Removes MapElement by its ID
	 * 
	 * @param _uniqueID
	 *            ID of the MapElement to be removed
	 * @return true if removing successed otherwise false
	 */
	public boolean removeMapElement(int _uniqueID) {
		for (int i = 0; i < getTowerOnMap().size(); i++) {
			if (getTowerOnMap().get(i).getUniqueID() == _uniqueID) {
				getTowerOnMap().remove(i);
				return true;
			}
		}
		return false;
	}

	/**
	 * Returns the CTowerDefenseElement with the given ID
	 * 
	 * @param _uniqueID
	 * @return CTowerDefenseElement if ID is found otherwise null
	 */
	public CTowerDefenseElement getMapElement(int _uniqueID) {
		for (int i = 0; i < getTowerOnMap().size(); i++) {
			if (getTowerOnMap().get(i).getUniqueID() == _uniqueID)
				return getTowerOnMap().get(i);
		}
		return null;
	}

	/**
	 * Checks free place in chararray representation of Map
	 * @param posX X position of the field
	 * @param posY Y position of the field
	 * @return true if mapElements is null or the place is free otherwise false
	 * @throws InvalidArgument If posX or posY is negative or bigger than maxX or maxY
	 */
	public boolean checkFreePlace(int posX, int posY) throws InvalidArgument {
			if (mapElements == null)
				return true;
			if (getMapElementValue(posX, posY) == '_')
				return true;
		return false;

	}
	
	/**
	 * Checks if tower is already positioned on the given coordinates in the Arraylist representation of builded towers.
	 * @param posX X position of field
	 * @param posY X position of field
	 * @return false if the position is occupied, true if Arraylist is empty or no tower found
	 * @throws InvalidArgument If Position is negative or bigger than maxX or maxY
	 */
	public boolean checkExTowerOnPlace(int posX, int posY) throws InvalidArgument {
		if (posX> mapElements.length || posX < 0
				|| posY > mapElements[0].length || posY < 0)
			throw new InvalidArgument();
		if (TowerOnMap.isEmpty())
			return true;
		for (int i = 0; i < getTowerOnMap().size(); i++) {
			if (getTowerOnMap().get(i).getPosX() == posX
					&& getTowerOnMap().get(i).getPosY() == posY)
				return false;
		}
		return true;
	}

	/**
	 * Checks if a pathelement is on the place with the coordinates: posX, posY 
	 * @param posX X coordinate of field
	 * @param posY Y coordinate of field
	 * @return true if path arraylist is empty or no pathelement found in given position otherwise false
	 * @throws InvalidArgument if Position is negative or bigger than maxX or maxY
	 */
	public boolean checkPathOnPlace(int posX, int posY) throws InvalidArgument {
		if (posX> mapElements.length || posX < 0
				|| posY > mapElements[0].length || posY < 0)
			throw new InvalidArgument();
		if (WayPaths.isEmpty())
			return true;
		for (int i = 0; i < WayPaths.size(); i++) {
			for (int j = 0; j < WayPaths.get(i).getMaxWayPoint(); j++) {
				if (WayPaths.get(i).getWayPoint(j).getX() == posX
						&& WayPaths.get(i).getWayPoint(j).getY() == posY)
					return false;
			}
		}
		return true;
	}
	
	public boolean checkTowerOnPlace(int x, int y) throws InvalidArgument
	{
		if(getMapElementValue(x, y) == 't')
			return true;
		return false;
			
	}

	/**
	 * Deletes all tower in Arraylist representation of builded towers
	 */
	public void flushAllTower() {
		getTowerOnMap().clear();
	}

	public void flushMapElements() {
		if(mapElements == null) return;
		for(int i =0;i<mapElements.length;i++)
			{
				for(int j = 0;j<mapElements[0].length;j++)
					mapElements[i][j] = '\u0000';
			}
	}
	public ArrayList<CTowerDefenseElement> getTowerOnMap() {
		return TowerOnMap;
	}

	public void setTowerOnMap(ArrayList<CTowerDefenseElement> towerOnMap) {
		TowerOnMap.addAll(towerOnMap);
		for(int i = 0; i<TowerOnMap.size();i++)
		{
			mapElements[TowerOnMap.get(i).getPosX()][TowerOnMap.get(i).getPosY()]= 't';
		}
	}

	public int getNbOfWayPaths() {
		return WayPaths.size();
	}
}
